/*
* ====================================================================
* Copyright (c) 2013-2014 Neudesic.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that redistributions of source
* retain the above copyright notice, this list of conditions and the
* following disclaimer.
*
* This source file includes software written by Neudesic, LLC (http://www.neudesic.com).
*
* THIS SOFTWARE IS PROVIDED BY Neudesic, LLC ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT, AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  FURTHER, IN NO EVENT SHALL
* Neudesic, LLC (THE AUTHOR) OR ANY CONTRIBUTORS BE LIABLE FOR ANY DAMAGES
* WHATSOEVER THAT ARE RELATED TO THE SOFTWARE [INCLUDING BUT NOT LIMITED TO,
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* SUCH AS LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION, HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)] EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGES.
*
* ====================================================================
*/
#ifndef __STREAMSOCKETCONTAINER_H__
#define __STREAMSOCKETCONTAINER_H__

#include "socketcontainer.h"
#include <wrl.h>
#include <windows.storage.streams.h>
#include <windows.networking.sockets.h>
#include <vector>

using Microsoft::WRL::ComPtr;
using ABI::Windows::Storage::Streams::IBuffer;
using ABI::Windows::Storage::Streams::IDataReader;
using ABI::Windows::Networking::Sockets::IStreamSocket;
using ABI::Windows::Networking::Sockets::IStreamSocketInformation;

namespace Neudesic {
	namespace Sockets {
		class StreamSocketContainer : public SocketContainer<IStreamSocket, IStreamSocketInformation>, public IStreamSocketContainer
		{
		public:
			StreamSocketContainer();
			virtual ~StreamSocketContainer(void);
#pragma region IStreamSocketContainer
			HRESULT GetSocketName(struct sockaddr* name, int* namelen) const;
			HRESULT SetSocketOptions(int level, int optname, const char* optval, int optlen);
		protected:
			HRESULT ConnectSocket(const ComPtr<IHostName>& hostName, const wchar_t* serviceName);
			HRESULT InternalReceive(char *buf, int bufferLength, int flags, LPDWORD lpNumberOfBytesRecvd);
#pragma endregion
		public:
			ISocketContainer* GetAcceptedSocket(struct sockaddr* addr, LPINT addrlen, LPCONDITIONPROC lpfnCondition, DWORD_PTR dwCallbackData);
			ComPtr<IDataReader> GetReader();
			HRESULT Listen(int backlog);
			HRESULT Receive(char *buf, int len, int flags, LPDWORD lpNumberOfBytesRecvd);
			HRESULT Send(const char* buf, unsigned int len, int flags, UINT32* numberOfBytesSent);
			HRESULT GetUnconsumedBufferLength(long command, ULONG* argp);
#pragma region Global use
			static int Select(fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timeval *timeout);
		protected:
			HRESULT StreamSocketContainer::PrepareDataReader(ComPtr<IBuffer>& buffer);
#pragma endregion
		private:
			static void AdjustCount(StreamSocketContainer* container, int* count, int timeInMs);
			HRESULT PrepareForMessages();
			HRESULT Send(const char* buf, unsigned int len, UINT32* numberOfBytesSent);
			void ReceiveThread(HANDLE enableReadEvent);
		private:
			bool receiveThreadStarted;
			ComPtr<IBuffer> receiveBuffer;
			ManualResetEvent bufferProcessed;
			ManualResetEvent readComplete;
			ManualResetEvent enableReadEvent;
		};
	}	//Sockets
}	//Neudesic

#endif
